#include "Block1.h"
#include "Block2.h"
#include "Block3.h"
#include "Block4.h"
#include "Block5.h"
#include "Block6.h"
#include "Block7.h"
#include "Game.h"

ColorGrid Game::sExistentBlocks[Const::GAME_BOARD_GRID_COUNT_V][Const::GAME_BOARD_GRID_COUNT_H];

/**
 * 构造方法。
 * @param gameBoard         游戏区域
 * @param gameBoardBitmap   游戏区域后台缓冲位图
 * @param preview           预览区域
 * @param previewBitmap     预览区域后台缓冲位图
 */
Game::Game(QWidget* gameBoard, QPixmap* gameBoardBitmap, QWidget* preview, QPixmap* previewBitmap) :
    mCurBlock(nullptr),
    mDifficulty(1),
    mEnableMusic(false),
    mGameBoard(gameBoard),
    mGameBoardBitmap(gameBoardBitmap),
    mMusicPlayer(Const::MUSIC_FILE_PATH),
    mNextBlock(nullptr),
    mPreview(preview),
    mPreviewBitmap(previewBitmap),
    mScore(0),
    mStatus(END),
    mTop(Const::GAME_BOARD_GRID_COUNT_V + 1) {
    // 画游戏区域
    this->drawGameBoard();

    // 画预览区域
    this->drawPreview();

    // 置随机数种子
    srand(static_cast<uint>(time(nullptr)));

    // 连接信号与槽
    QObject::connect(&mTimer, SIGNAL(timeout()), SLOT(onTimer()));

    // 设置音乐播放模式为无限循环
    mMusicPlayer.setLoops(QSound::Infinite);
}

/**
 * 析构方法。
 */
Game::~Game() {
    if (mCurBlock != nullptr) {
        delete mCurBlock;
    }
    if (mNextBlock != nullptr) {
        delete mNextBlock;
    }
}

/**
 * 检查是否存在满行。
 */
void Game::checkRow() {
    // 更新"有方块的最高行"
    int top = mCurBlock->getTop();
    if (top < mTop) {
        mTop = top;
    }

    int released = 0;   // 消除的行数
    int bottom = mCurBlock->getBottom();

    // 从有方块的最上面一行，到当前方块所占的最下一行，逐行检查
    for (int i = mTop; i <= bottom; i++) {
        for (int j = 0; j < Const::GAME_BOARD_GRID_COUNT_H; j++) {
            // 如果该行有空的，就跳过该行
            if (!sExistentBlocks[i][j].mUsed) {
                break;
            }
            // 如果该行满了，消除该行
            if (j == Const::GAME_BOARD_GRID_COUNT_H - 1) {
                this->releaseRow(i);
                released++;
            }
        }
    }

    if (released > 0) {
        // 因为消行了，所以需要重绘
        this->drawGameBoard();
        this->drawExistentBlock();

        // 计算得分
        mScore += released * released * 100;

        // 发送通知
        emit UM_SCORE(mScore);

        // 计算难度
        if (mScore / Const::DIFF_BASE + 1 > mDifficulty && mDifficulty < Const::DIFF_MAX) {
            // 增加难度
            mDifficulty++;
            mTimer.setInterval(Const::TIMER_INIT - Const::TIMER_GROW * (mDifficulty - 1));

            // 发送通知
            emit UM_DIFFICULTY_UP(mDifficulty);
        }
    }
}

/**
 * 随机创建一种方块。
 * @return 方块
 */
Block* Game::createRandBlock() {
    int random = rand() % 7 + 1;

    switch (random) {
        case 1:
            return new Block1();
        case 2:
            return new Block2();
        case 3:
            return new Block3();
        case 4:
            return new Block4();
        case 5:
            return new Block5();
        case 6:
            return new Block6();
        case 7:
            return new Block7();
    }

    return new Block1();
}

/**
 * 画累积的方块。
 */
void Game::drawExistentBlock() {
    mDrawer.begin(mGameBoardBitmap);
    for (int i = 0; i < Const::GAME_BOARD_GRID_COUNT_V; i++) {
        for (int j = 0; j < Const::GAME_BOARD_GRID_COUNT_H; j++) {
            if (sExistentBlocks[i][j].mUsed) {
                mDrawer.fillRect(Const::GRID_SIZE * j, Const::GRID_SIZE * i, Const::GRID_SIZE, Const::GRID_SIZE, sExistentBlocks[i][j].mColor);
                Block::drawEdge(&mDrawer, Const::GRID_SIZE * j, Const::GRID_SIZE * i);
            }
        }
    }
    mDrawer.end();
}

/**
 * 画游戏区域。
 */
void Game::drawGameBoard() {
    mDrawer.begin(mGameBoardBitmap);
    mDrawer.fillRect(0, 0, Const::GAME_BOARD_WIDTH, Const::GAME_BOARD_HEIGHT, Const::GAME_BOARD_COLOR);
    mDrawer.end();
    mGameBoard->update();
}

/**
 * 画预览区域。
 */
void Game::drawPreview() {
    mDrawer.begin(mPreviewBitmap);
    mDrawer.fillRect(0, 0, Const::PREVIEW_WIDTH, Const::PREVIEW_HEIGHT, Const::PREVIEW_COLOR);
    mDrawer.end();
    if (mNextBlock != nullptr) {
        mNextBlock->enterPreview(mPreviewBitmap);
    }
    mPreview->update();
}

/**
 * 游戏结束。
 * @param isWon 胜利则为 true，否则为 false
 */
void Game::gameOver(bool isWon) {
    // 游戏停止
    this->stop();

    // 发送通知
    emit UM_GAME_OVER(isWon);
}

/**
 * 获取游戏状态。
 * @return 状态
 */
Game::Status Game::getStatus() {
    return mStatus;
}

/**
 * 判断是否失败。
 * @return 失败则返回 true，否则返回 false
 */
bool Game::isLost() {
    return mTop == 0 && sExistentBlocks[0][4].mUsed;
}

/**
 * 判断是否胜利。
 * @return 胜利则返回 true，否则返回 false
 */
bool Game::isWon() {
    return mScore >= Const::WIN_NUMBER;
}

/**
 * 方块下落。
 */
void Game::moveBlockDown() {
    if (mStatus != PLAYING) {
        return;
    }

    bool canMoveDown = mCurBlock->moveDown();
    if (!canMoveDown) {
        // 检查是否存在满行
        this->checkRow();

        // 判断是否胜利
        bool isWon = this->isWon();
        if (isWon) {
            this->gameOver(true);
            return;
        }

        // 判断是否失败
        bool isLost = this->isLost();
        if (isLost) {
            this->gameOver(false);
            return;
        }

        // 产生新的方块
        this->nextBlock();
    }
}

/**
 * 产生新的方块。
 */
void Game::nextBlock() {
    // 创建当前方块
    if (mCurBlock != nullptr) {
        delete mCurBlock;
        mCurBlock = mNextBlock;
    } else {
        mCurBlock = this->createRandBlock();
    }

    // 创建下一个方块
    mNextBlock = this->createRandBlock();

    // 当前方块进入游戏区域
    mCurBlock->enterGameBoard(mGameBoard, mGameBoardBitmap);

    // 下一个方块进入预览区域
    this->drawPreview();
}

/**
 * 按键事件的响应方法。
 * @param key  按键
 */
void Game::onKeyPressed(int key) {
    if (mStatus != PLAYING) {
        return;
    }

    switch (key) {
        case Qt::Key_Down:
            this->moveBlockDown();
            break;
        case Qt::Key_Left:
            mCurBlock->moveLeft();
            break;
        case Qt::Key_Right:
            mCurBlock->moveRight();
            break;
        case Qt::Key_Up:
            mCurBlock->transform();
            break;
    }
}

/**
 * 定时器事件的响应方法。
 */
void Game::onTimer() {
    this->moveBlockDown();
}

/**
 * 游戏暂停。
 */
void Game::pause() {
    if (mStatus == PLAYING) {
        // 停止定时器
        mTimer.stop();

        // 停止播放音乐
        if (mEnableMusic) {
            mMusicPlayer.stop();
        }

        // 将状态设置为已暂停
        mStatus = PAUSED;
    } else if (mStatus == PAUSED) {
        // 开启定时器
        mTimer.start(Const::TIMER_INIT - Const::TIMER_GROW * (mDifficulty - 1));

        // 播放音乐
        if (mEnableMusic) {
            mMusicPlayer.play();
        }

        // 将状态设置为游戏中
        mStatus = PLAYING;
    }
}

/**
 * 播放音乐。
 * @return 正在播放音乐则为 true，否则为 false
 */
bool Game::playMusic() {
    mEnableMusic = !mEnableMusic;

    if (mEnableMusic) {
        if (mStatus == PLAYING) {
            mMusicPlayer.play();
        }
    } else {
        mMusicPlayer.stop();
    }

    return mEnableMusic;
}

/**
 * 消行。
 * @param row  行号
 */
void Game::releaseRow(int row) {
    // 最上行
    if (row == 0) {
        for (int j = 0; j < Const::GAME_BOARD_GRID_COUNT_H; j++) {
            sExistentBlocks[row][j].mUsed = false;
        }
        mTop++;
        return;
    }

    // 非最上行
    for (int i = row; i > mTop; i--) {
        for (int j = 0; j < Const::GAME_BOARD_GRID_COUNT_H; j++) {
            sExistentBlocks[i][j] = sExistentBlocks[i - 1][j];
        }
    }

    for (int j = 0; j < Const::GAME_BOARD_GRID_COUNT_H; j++) {
        sExistentBlocks[mTop][j].mUsed = false;
    }

    mTop++;
}

/**
 * 游戏开始。
 */
void Game::start() {
    // 画游戏区域
    this->drawGameBoard();

    // 画预览区域
    this->drawPreview();

    // 创建方块
    memset(sExistentBlocks, 0, sizeof(sExistentBlocks));
    if (mCurBlock != nullptr) {
        delete mCurBlock;
        mCurBlock = nullptr;
    }
    if (mNextBlock != nullptr) {
        delete mNextBlock;
        mNextBlock = nullptr;
    }
    this->nextBlock();

    // 重置难度
    mDifficulty = 1;

    // 重置分数
    mScore = 0;

    // 开启定时器
    mTimer.stop();
    mTimer.start(Const::TIMER_INIT);

    // 播放音乐
    if (mEnableMusic) {
        mMusicPlayer.play();
    }

    // 将状态设置为游戏中
    mStatus = PLAYING;
}

/**
 * 游戏停止。
 */
void Game::stop() {
    // 将状态设置为已结束
    mStatus = END;

    // 停止定时器
    mTimer.stop();

    // 停止播放音乐
    if (mEnableMusic) {
        mMusicPlayer.stop();
    }
}
